package com.grgbanking.encryp.service;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.grgbanking.encryp.bean.KeyInfo;
import com.grgbanking.encryp.constant.BusinessConst;
import com.grgbanking.encryp.constant.ErrorCode;
import com.grgbanking.encryp.util.SmUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;


@Component
@Scope("prototype")
@Slf4j
public class BusinessLogic {

    @Autowired
    private KeyInfoService keyinfoService;

    @Value("${infinitive.sm4contextStr}")
    private String paramStr;
    @Value("${infinitive.sm2hostPkVND}")
    private String sm2hostpk;
    @Value("${infinitive.sm2hostSkVnd}")
    private String sm2hostsk;

    private final ObjectMapper objectMapper;
    public BusinessLogic() {
        this.objectMapper = new ObjectMapper();
    }


    public String getKeyMSK(JsonNode bodyNode) throws Exception {
        String keyPuk = bodyNode.get("keyPuk").asText();
        String keySN = bodyNode.get("keySN").asText();
        String delete = bodyNode.get("delete").asText();

        if (keyPuk != null && keySN != null) {
            String masterKey= null;
            if (delete.equalsIgnoreCase("N")){
                masterKey= RandomStringUtils.random(32, "ABCDEF0123456789");
            }else {
                masterKey = keyinfoService.getByName(keySN).getMasteKey();
            }

            SmUtil.sm2Encrypt(masterKey, keyPuk);
            String lockKey = SmUtil.sm2Encrypt(masterKey,keyPuk).substring(2).toUpperCase();//为什么去前2位？硬件芯片不需要前面的标志位。
            String dbStoreMkey = SmUtil.sm2Encrypt(masterKey,sm2hostpk).substring(2).toUpperCase();
            String smMac = SmUtil.encryptSM4_ECB(masterKey, paramStr).substring(0,16).toUpperCase();

            KeyInfo keyinfo = new KeyInfo();
            keyinfo.setName(keySN);
            keyinfo.setLockKey(lockKey);
            keyinfo.setLockKeyMac(smMac);
            keyinfo.setMasteKey(masterKey);
            keyinfo.setStoreKey(dbStoreMkey);

            if (delete.equals("N")){
                keyinfo.setDeleteflag("N");
                keyinfoService.save(keyinfo);
            }else if (delete.equals("D")){
                keyinfoService.updateDeletingByName(keySN);
            }else {
                keyinfoService.deleteByName(keySN); //物理删除，运算还能继续进行!但会钥匙校验不过，还是最终确认删除。
            }

            ObjectNode returnNode = objectMapper.createObjectNode();
            returnNode.put("msgType", BusinessConst.MSG_TYPE_getKeyMSK);

            ObjectNode msgbody = objectMapper.createObjectNode();
            msgbody.put("lockKey", lockKey);
            msgbody.put("smMac", smMac);
            returnNode.set("msgbody", msgbody);
            return returnNode.toString();
        }
        return parseMsgError(BusinessConst.MSG_TYPE_getKeyMSK, ErrorCode.ERROR_NULL_PARAM);
    }

    public String getKeyWKS(JsonNode bodyNode) throws Exception {
        String workKeyString = bodyNode.get("workKeyString").asText();
        String keySN = bodyNode.get("keySN").asText();

        if (keySN == null) {
            if (StrUtil.isEmpty(workKeyString)){
                workKeyString = RandomStringUtils.random(32, "ABCDEF0123456789").toUpperCase();//工作密钥
            }
//            List<ElockMasterKey> MasterKeys = elockMasterKeyService.find(cashBoxLockKey.getSerialNumber(), ElockMasterKeyService.TYPE_N);
//            String masterKey = MasterKeys.get(0).getMasterKey();
            KeyInfo keyinfo = keyinfoService.getByName(keySN);

            String workKey = SmUtil.encryptSM4_ECB(keyinfo.getMasteKey(), workKeyString);//主密钥对工作密钥再sm4
            String workKeyMac = SmUtil.encryptSM4_ECB(workKeyString, paramStr).substring(0, 16);

            ObjectNode returnNode = objectMapper.createObjectNode();
            returnNode.put("msgType", BusinessConst.MSG_TYPE_getKeyMSK);

            ObjectNode msgbody = objectMapper.createObjectNode();
            msgbody.put("workKeyString", workKeyString);
            msgbody.put("workKey", workKey);
            msgbody.put("smMac", workKeyMac);
            returnNode.set("msgbody", msgbody);
            return returnNode.toString();
        }
        return parseMsgError(BusinessConst.MSG_TYPE_getKeyWKS, ErrorCode.ERROR_NULL_PARAM);
    }

    public String getLockMSK(JsonNode bodyNode) throws Exception {
        try{
            String lockPuk = bodyNode.get("lockPuk").asText();
            String lockSN = bodyNode.get("lockSN").asText();
            String delete = bodyNode.get("delete").asText();

            if (lockPuk != null && lockSN != null) {
                String masterKey = null;
                if (delete.equalsIgnoreCase("N")) {
                    masterKey = RandomStringUtils.random(32, "ABCDEF0123456789");
                } else {
                    masterKey = keyinfoService.getByName(lockSN).getMasteKey();
                }

                SmUtil.sm2Encrypt(masterKey, lockPuk);
                String lockKey = SmUtil.sm2Encrypt(masterKey, lockPuk).substring(2).toUpperCase();//为什么去前2位？硬件芯片不需要前面的标志位。
                String dbStoreMkey = SmUtil.sm2Encrypt(masterKey, sm2hostpk).substring(2).toUpperCase();
                String smMac = SmUtil.encryptSM4_ECB(masterKey, paramStr).substring(0, 16).toUpperCase();

                KeyInfo keyinfo = new KeyInfo();
                keyinfo.setName(lockSN);
                keyinfo.setLockKey(lockKey);
                keyinfo.setLockKeyMac(smMac);
                keyinfo.setMasteKey(masterKey);
                keyinfo.setStoreKey(dbStoreMkey);

                if (delete.equals("N")) {
                    keyinfo.setDeleteflag("N");
                    keyinfoService.save(keyinfo);
                } else if (delete.equals("D")) {
                    keyinfoService.updateDeletingByName(lockSN);
                } else {
                    keyinfoService.deleteByName(lockSN); //物理删除，运算还能继续进行!但会钥匙校验不过，还是最终确认删除。
                }

                ObjectNode returnNode = objectMapper.createObjectNode();
                returnNode.put("msgType", BusinessConst.MSG_TYPE_getKeyMSK);

                ObjectNode msgbody = objectMapper.createObjectNode();
                msgbody.put("lockKey", lockKey);
                msgbody.put("smMac", smMac);
                returnNode.set("msgbody", msgbody);
                return returnNode.toString();
            }
            return parseMsgError(BusinessConst.MSG_TYPE_getLockMSK, ErrorCode.ERROR_NULL_PARAM);
        }catch(Exception e){
            return parseMsgError(BusinessConst.MSG_TYPE_getLockMSK, ErrorCode.ERROR_ProcMsgDetailException.getCode(),e.getMessage());
        }
    }

    public String encryptTask(JsonNode bodyNode) {
        try{
            String dataStr = bodyNode.get("data").asText();
            String keySN = bodyNode.get("keySN").asText();
            String workkeyRandom = bodyNode.get("workkeyRandom").asText();

            if (dataStr != null && keySN != null && workkeyRandom!= null){
                int i=0,j=128,len=128;
                if(dataStr.length() % 128 != 0){
                    dataStr = StrUtil.padAfter(dataStr.substring(i), 128*(dataStr.length() / 128 +1 ), '0');
                }
                String sm4 = SmUtil.encryptSM4_ECB(workkeyRandom,dataStr);
                int length = dataStr.length();
                List<String> list = new ArrayList<>();
                while (length != 0){
                    String s = SmUtil.sm3(dataStr.substring(i,j));
                    list.add(s);
                    i += len;
                    j += len;
                    length -= len;
                }

                String sm3 = list.get(0);
                for(int a = 1; a < list.size(); a++){
                    String sb= SmUtil.getSm3Sb(sm3, list.get(a));
                    sm3 = sb;
                }

                ObjectNode returnNode = objectMapper.createObjectNode();
                returnNode.put("msgType", BusinessConst.MSG_TYPE_encryptTask);

                ObjectNode msgbody = objectMapper.createObjectNode();
                msgbody.put("smdata", sm4);
                msgbody.put("smMac", sm3);
                returnNode.set("msgbody", msgbody);
                return returnNode.toString();
            }else {
                return parseMsgError(BusinessConst.MSG_TYPE_encryptTask, ErrorCode.ERROR_NULL_PARAM);
            }
        }catch(Exception e){
            log.error("encryptTask error:",e);
            return parseMsgError(BusinessConst.MSG_TYPE_encryptTask, ErrorCode.ERROR_ProcMsgDetailException.getCode(),e.getMessage());
        }
    }

    public String process(String msg) {
        String response="";
        String msgType="";
        try {
            JsonNode rootNode = objectMapper.readTree(msg);
            msgType = rootNode.get("msgType").asText();
            JsonNode bodyNode = rootNode.get("msgbody");

            switch (msgType) {
                case "getKeyMSK":
                    response = getKeyMSK(bodyNode);
                    break;
                case "getKeyWKS":
                    response = getKeyWKS(bodyNode);
                    break;
                case "getLockMSK":
                    response = getLockMSK(bodyNode);
                    break;
                case "encryptTask":
                    response = encryptTask(bodyNode);
                    break;
                default:
                    response = parseMsgError(msgType, ErrorCode.ERROR_UnknownMsgType);
            }
        } catch (Exception e) {
            log.error("process exception: ",e);
            response = parseMsgError(BusinessConst.MSG_TYPE_Exception,ErrorCode.ERROR_Exception.getCode(),e.getMessage());
        }

        return response;
    }

    private String parseMsgError(String msgType, ErrorCode errorCode) {
        return parseMsgError(msgType, errorCode.getCode(), errorCode.getMessage());
    }

    private String parseMsgError(String type,String code, String info) {
        ObjectNode returnNode = objectMapper.createObjectNode();
        returnNode.put("msgType", type);
        returnNode.put("respCode", code);
        returnNode.put("respInfo", info);
        return returnNode.toString();
    }
}
